package com.googlecode.derby.customize;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import org.apache.commons.lang.StringUtils;

import com.googlecode.derby.customize.annotation.Column;
import com.googlecode.derby.customize.annotation.Table;
import com.googlecode.derby.customize.exception.DbInfoException;

/**
 * Derby情報などDerbyと接続および操作を行うクラス.
 * 
 * @author o.tanaka
 * @version 2009/12/10
 */
public class DerbyInfo implements DbInfo {

    private long executeUpdateCnt = 0;

    private Connection con = null;

    private Statement stmt = null;

    private PreparedStatement pstmt = null;

    private CallableStatement cstmt = null;

    private ResultSet rs = null;

    private final String dbname;

    private final String user;

    private final String password;

    private final String key;

    private final SqlMaker sqlMaker = new DerbySqlMaker();

    private final OutData outData = new OutData();

    private boolean createTableCheck = false;

    private List<ResultData> resultDatas = null;

    /**
     * @param aDbname
     *            database name
     * @param aUser
     *            user
     * @param aPassword
     *            password
     * @param aKey
     *            key
     */
    public DerbyInfo(
            final String aDbname,
            final String aUser,
            final String aPassword,
            final String aKey) {
        this.dbname = aDbname;
        this.user = aUser;
        this.password = aPassword;
        this.key = aKey;
    }

    /**
     *
     */
    private void autoSqlClear() {
        if (this.sqlMaker.getAutoSql() != null) {
            this.sqlMaker.getSelectBindCountMap().clear();
            this.sqlMaker.getWhereBindDataz().clear();
            this.sqlMaker.getAutoSql().setQuery(null);
            this.sqlMaker.getAutoSql().setWhere(null);
            this.sqlMaker.getAutoSql().setOrderby(null);
            if (this.isConnectionClose()) {
                this.connection();
            }
        }
    }

    /**
     * @param aWhereBindDataz
     */
    private void bindData(final List<BindData> aWhereBindDataz) {
        try {
            for (final BindData bindData : aWhereBindDataz) {
                final String type = bindData.getType();
                final int sqlType = this.typeConv(type);
                final Integer index = bindData.getIndex();
                final Object value = bindData.getValue();
                switch (sqlType) {
                case Types.VARCHAR:
                    this.pstmt.setString(index.intValue(),
                            String
                            .valueOf(value));
                    break;
                case Types.CHAR:
                    this.pstmt.setString(index.intValue(),
                            String
                            .valueOf(value));
                    break;
                case Types.INTEGER:
                    this.pstmt.setInt(
                            index.intValue(),
                            Integer.class.cast(value)
                            .intValue());
                    break;
                case Types.NUMERIC:
                    this.pstmt.setLong(
                            index.intValue(),
                            Long.class.cast(value).longValue());
                    break;
                case Types.TIMESTAMP:
                    this.pstmt.setTimestamp(
                            index.intValue(),
                            Timestamp.class.cast(value));
                    break;
                case Types.DATE:
                    this.pstmt.setDate(
                            index.intValue(),
                            Date.class.cast(value));
                    break;
                case Types.DOUBLE:
                    this.pstmt.setDouble(
                            index.intValue(),
                            Double.class.cast(value)
                            .doubleValue());
                    break;
                case Types.FLOAT:
                    this.pstmt.setFloat(
                            index.intValue(),
                            Float.class.cast(value)
                            .floatValue());
                    break;
                default:
                    this.pstmt.setObject(
                            index.intValue(),
                            value);
                    break;
                }
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "bindData error:",
                    this, e);
        }
    }

    private void bindDataMap(
            final Map aBindCountMap
            , final Object aObject) {
        final Method[] methodz = aObject.getClass().getMethods();
        try {
            for (final Method method : methodz) {
                final Column column = method.getAnnotation(Column.class);
                if (column != null) {
                    final String indexStr =
                        aBindCountMap.get(column.value()[0]) == null
                        ? null
                        : aBindCountMap.get(column.value()[0]).toString();
                    if (indexStr != null) {
                        final String type = column.value()[1];
                        final int sqlType = this.typeConv(type);
                        final Integer index = Integer.valueOf(indexStr);
                        final Object value = method.invoke(aObject);
                        if (value != null) {
                            switch (sqlType) {
                            case Types.VARCHAR:
                                this.pstmt.setString(index.intValue(),
                                        String
                                        .valueOf(value));
                                break;
                            case Types.CHAR:
                                this.pstmt.setString(index.intValue(),
                                        String
                                        .valueOf(value));
                                break;
                            case Types.INTEGER:
                                this.pstmt.setInt(
                                        index.intValue(),
                                        Integer.class.cast(value)
                                        .intValue());
                                break;
                            case Types.NUMERIC:
                                this.pstmt.setLong(
                                        index.intValue(),
                                        Long.class.cast(value).longValue());
                                break;
                            case Types.TIMESTAMP:
                                this.pstmt.setTimestamp(
                                        index.intValue(),
                                        Timestamp.class.cast(value));
                                break;
                            case Types.DATE:
                                this.pstmt.setDate(
                                        index.intValue(),
                                        Date.class.cast(value));
                                break;
                            case Types.DOUBLE:
                                this.pstmt.setDouble(
                                        index.intValue(),
                                        Double.class.cast(value)
                                        .doubleValue());
                                break;
                            case Types.FLOAT:
                                this.pstmt.setFloat(
                                        index.intValue(),
                                        Float.class.cast(value)
                                        .floatValue());
                                break;
                            default:
                                this.pstmt.setObject(
                                        index.intValue(),
                                        value);
                                break;
                            }
                        } else {
                            this.pstmt.setNull(index.intValue(), sqlType);
                        }
                    }
                }
            }
        } catch (final NumberFormatException e) {
            throw new DbInfoException(
                    "bindDataMap error:",
                    this, e);
        } catch (final IllegalArgumentException e) {
            throw new DbInfoException(
                    "bindDataMap error:",
                    this, e);
        } catch (final IllegalAccessException e) {
            throw new DbInfoException(
                    "bindDataMap error:",
                    this, e);
        } catch (final InvocationTargetException e) {
            throw new DbInfoException(
                    "bindDataMap error:",
                    this, e);
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "bindDataMap error:",
                    this, e);
        }
    }

    /**
     * callableStatementメソッド.
     * 
     * @return DbInfo
     */
    public final DbInfo callableStatement() {
        try {
            if (this.sqlMaker.getQuery() != null) {
                this.stmt = null;
                this.pstmt = null;
                this.cstmt = this.con.prepareCall(this.sqlMaker.getQuery());
            } else {
                throw new SQLException("query is null");
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "callableStatement error:",
                    this, e);
        }
        return this;
    }

    /**
     * Connection.closeメソッド.
     */
    public final void close() {
        try {
            if (this.con != null) {
                if (this.rs != null) {
                    this.rs.close();
                    this.rs = null;
                }
                if (this.stmt != null) {
                    this.stmt.close();
                    this.stmt = null;
                } else if (this.pstmt != null) {
                    this.pstmt.close();
                    this.pstmt = null;
                } else if (this.cstmt != null) {
                    this.cstmt.close();
                    this.cstmt = null;
                }
                this.con.close();
                this.con = null;
                DbManager.getConnection(
                        this.dbname, DbManager.SHUTDOWN, this.key);
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "close error:", e);
        }
    }

    /**
     * commitメソッド.
     * 
     * @return DbInfo
     */
    public final DbInfo commit() {
        try {
            if (!this.isConnectionClose()) {
                if (!this.con.getAutoCommit()) {
                    this.con.commit();
                }
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "commit error:",
                    this, e);
        }
        return this;
    }

    /**
     * Connectionメソッド.
     */
    public final void connection() {
        if (StringUtils.isNotEmpty(this.user)) {
            this.con = DbManager.getConnection(
                    this.dbname,
                    DbManager.CREATE,
                    this.user,
                    this.password,
                    this.key);
        } else {
            this.con = DbManager.getConnection(
                    this.dbname, DbManager.CREATE, this.key);
        }
    }

    /**
     * createStatementメソッド.
     * 
     * @return DbInfo
     */
    public final DbInfo createStatement() {
        try {
            if (this.sqlMaker.getQuery() != null) {
                this.pstmt = null;
                this.cstmt = null;
                this.stmt = this.con.createStatement();
            } else {
                throw new SQLException("query is null");
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "createStatement error:",
                    this, e);
        }
        return this;
    }

    /**
     */
    private boolean createTable() {
        if (this.sqlMaker.getAutoSql() != null) {
            this.sqlMaker.setQuery(this.sqlMaker.getAutoSql().getCreateTable());
            this.createStatement();
            this.execute();
            return true;
        }
        throw new DbInfoException(
                "createTable error:",
                this, new NullPointerException());
    }

    /**
     * @param aList data class
     */
    public void delete(final List<?> aList) {
        if ((aList != null) && (aList.size() != 0)) {
            final Class<?> clazz = aList.get(0).getClass();
            final Table table = Table.class.cast(
                    clazz.getAnnotation(Table.class));
            String tableName = "";
            if (table.value().length >= 1) {
                tableName = table.value()[0];
            }
            this.sqlMaker.setAutoSql(
                    this.sqlMaker.delete(tableName, clazz));
            final Map deleteBindCountMap =
                this.sqlMaker.getDeleteBindCountMap();
            long datacount = 0;
            if (this.isConnectionClose()) {
                this.connection();
            }
            for (int i = 0; i < aList.size(); i++) {
                this.sqlMaker.setQuery(
                        this.sqlMaker.getAutoSql().getDelete());
                this.sqlMaker.setBindData(aList.get(i));
                this.preparedStatement();
                this.bindDataMap(deleteBindCountMap, aList.get(i));
                final long count = this.execute();
                datacount += count;
            }
            this.executeUpdateCnt = datacount;
        }
    }

    /**
     * executeUpdateメソッド.
     * 
     * @return DbInfo
     */
    private long execute() {
        long cnt = 0;
        try {
            if (this.stmt != null) {
                cnt =
                    this.stmt.executeUpdate(this.sqlMaker.getQuery());
            } else if (this.pstmt != null) {
                cnt = this.pstmt.executeUpdate();
            } else if (this.cstmt != null) {
                cnt = this.cstmt.executeUpdate();
            } else {
                throw new SQLException("Statement is null");
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "execute error:",
                    this, e);
        }
        return cnt;
    }

    /**
     * @return autoCommit
     */
    public boolean getAutoCommit() {
        try {
            if (this.con != null) {
                return this.con.getAutoCommit();
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "getAutoCommit error:",
                    this, e);
        }
        return false;
    }

    /**
     * @return Object
     */
    public Object getBindData() {
        return this.sqlMaker.getBindData();
    }

    /**
     * @return dbname
     */
    public final String getDbname() {
        return this.dbname;
    }

    /**
     * executeUpdateの更新件数取得メソッド.
     * 
     * @return UPDATEまたはDELETEの件数を取得
     */
    public final long getExecuteUpdateCnt() {
        return this.executeUpdateCnt;
    }

    /**
     * @return password
     */
    public final String getPassword() {
        return this.password;
    }

    /**
     * @return query
     */
    public String getQuery() {
        return this.sqlMaker.getQuery();
    }

    /**
     * @return resultDatas
     */
    public final List<ResultData> getResultDatas() {
        this.preparedQuery();
        this.srchMapData();
        return this.resultDatas;
    }

    /**
     * @param maxCount
     *            最大件数
     * @return resultDatas
     */
    public final List<ResultData> getResultDatas(final long maxCount) {
        // TODO rownum SQL 作成
        this.preparedQuery();
        this.srchMapData(maxCount);
        return this.resultDatas;
    }

    /**
     * @param bgnRownum
     *            開始件数
     * @param endRownum
     *            終了件数
     * @return resultDatas
     */
    public final List<ResultData> getResultDatas(
            final long bgnRownum, final long endRownum) {
        // TODO rownum SQL 作成
        this.preparedQuery();
        this.srchMapData(bgnRownum, endRownum);
        return this.resultDatas;
    }

    /**
     * @return user
     */
    public final String getUser() {
        return this.user;
    }

    /**
     * @return List<BindData>
     */
    public List<BindData> getWhereBindDataz() {
        return this.sqlMaker.getWhereBindDataz();
    }

    /**
     * @param aCreate
     *            DbConst.CREATE/DbConst.NOT_CREATE
     * @return true/false
     */
    private boolean hasTable(
            final String aCreate) {
        if (this.sqlMaker.getAutoSql() != null) {
            try {
                if (this.isConnectionClose()) {
                    this.connection();
                }
                this.select(null).getResultDatas(1);
            } catch (final DbInfoException e) {
                if (DbConst.CREATE.equals(aCreate)) {
                    this.connection();
                    this.createTableCheck = this.createTable();
                    return this.createTableCheck;
                }
                throw new DbInfoException(
                        "select error:",
                        this, e);
            }
            this.statementClose();
            return true;
        }
        throw new DbInfoException(
                "hasTable error:",
                this, new NullPointerException());
    }

    /**
     * @param aList
     *            bind data
     * @param aNotOverride
     *            DbConst.NOT_OVERRIDE
     * @return DbInfo
     *      java.lang.String)
     */
    public DbInfo insertUpdate(final List<?> aList, final String aNotOverride) {
        if ((aList != null) && (aList.size() != 0)) {
            final Class<?> clazz = aList.get(0).getClass();
            final Table table = Table.class.cast(
                    clazz.getAnnotation(Table.class));
            String tableName = "";
            if (table.value().length >= 1) {
                tableName = table.value()[0];
            }
            this.sqlMaker.setAutoSql(
                    this.sqlMaker.insertUpdate(tableName, clazz));
            this.sqlMaker.getSelectBindCountMap();
            final Map updateBindCountMap =
                this.sqlMaker.getUpdateBindCountMap();
            final Map insertBindCountMap =
                this.sqlMaker.getInsertBindCountMap();
            long datacount = 0;
            for (int i = 0; i < aList.size(); i++) {
                boolean dataUpdate = true;
                if (DbConst.NOT_OVERRIDE.equals(aNotOverride)) {
                    this.sqlMaker.setQuery(
                            this.sqlMaker.getAutoSql().getQuery());
                    this.sqlMaker.setBindData(aList.get(i));
                    if (this.getResultDatas().size() > 0) {
                        dataUpdate = false;
                    }
                    if (this.isConnectionClose()) {
                        this.connection();
                    }
                }
                long count = 0;
                if (this.sqlMaker.getUpdateColumnCount() == 0) {
                    this.sqlMaker.setQuery(
                            this.sqlMaker.getAutoSql().getDelete());
                    this.preparedStatement();
                    this.bindDataMap(insertBindCountMap, aList.get(i));
                    this.execute();
                } else {
                    if (dataUpdate) {
                        if (StringUtils.isNotEmpty(
                                this.sqlMaker.getAutoSql().getWhere())) {
                            this.sqlMaker.setQuery(
                                    this.sqlMaker.getAutoSql().getUpdate());
                            this.preparedStatement();
                            this.bindDataMap(updateBindCountMap, aList.get(i));
                            count = this.execute();
                        }
                    }
                }
                if (count == 0) {
                    this.sqlMaker.setQuery(
                            this.sqlMaker.getAutoSql().getInsert());
                    this.preparedStatement();
                    this.bindDataMap(insertBindCountMap, aList.get(i));
                    count = this.execute();
                }
                datacount += count;
            }
            this.executeUpdateCnt = datacount;
        }
        return this;
    }

    /**
     * @return true/false
     */
    public boolean isConnectionClose() {
        boolean ret = false;
        try {
            ret = ((this.con == null)
                || ((this.con != null) && this.con.isClosed()));
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "isConnectionClose error:",
                    this, e);
        }
        return ret;
    }

    /**
     * @return create table is true.
     */
    public boolean isCreateTable() {
        try {
            if (this.con != null) {
                if (this.con.getAutoCommit()) {
                    this.close();
                }
            }
            return this.createTableCheck;
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "isCreateTable error:",
                    this, e);
        }
    }

    /**
     * @param aOrderbyColumn
     *            order by column
     * @return DbInfo
     */
    public DbInfo orderby(final String aOrderbyColumn) {
        this.sqlMaker.addOrderby(aOrderbyColumn);
        return this;
    }

    /**
     *
     */
    private void preparedQuery() {
        if (this.sqlMaker.getSelectBindCountMap().isEmpty()
            && this.sqlMaker.getWhereBindDataz().isEmpty()) {
            if (StringUtils.isNotEmpty(
                    this.sqlMaker.getAutoSql().getOrderby())) {
                this.sqlMaker.setQuery(this.sqlMaker.getQuery()
                    + " "
                    + this.sqlMaker.getAutoSql().getOrderby());
            }
            if (StringUtils.isEmpty(this.sqlMaker.getQuery())) {
                this.sqlMaker.setQuery(this.sqlMaker.getAutoSql().getSelect());
            }
            this.createStatement();
        } else {
            if (StringUtils.isEmpty(
                    this.sqlMaker.getAutoSql().getQuery())) {
                final StringBuilder query =
                    new StringBuilder(
                        this.sqlMaker.getAutoSql().getSelect());
                if (StringUtils.isNotEmpty(
                        this.sqlMaker.getAutoSql().getWhere())) {
                    query.append(" ");
                    query.append(this.sqlMaker.getAutoSql().getWhere());
                }
                if (StringUtils.isNotEmpty(
                        this.sqlMaker.getAutoSql().getOrderby())) {
                    query.append(" ");
                    query.append(this.sqlMaker.getAutoSql().getOrderby());
                }
                this.sqlMaker.setQuery(query.toString());
            }
            this.preparedStatement();
            if (this.sqlMaker.getSelectBindCountMap().size() > 0) {
                this.bindDataMap(
                        this.sqlMaker.getSelectBindCountMap(),
                        this.sqlMaker.getBindData());
            } else if (this.sqlMaker.getWhereBindDataz().size() > 0) {
                this.bindData(this.sqlMaker.getWhereBindDataz());
            }
        }
    }

    /**
     * preparedStatementメソッド.
     * 
     * @return DbInfo
     */
    public final DbInfo preparedStatement() {
        try {
            if (this.sqlMaker.getQuery() != null) {
                this.stmt = null;
                this.cstmt = null;
                this.pstmt =
                    this.con.prepareStatement(this.sqlMaker.getQuery());
            } else {
                throw new SQLException("query is null");
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "preparedStatement error:",
                    this, e);
        }
        return this;
    }

    /**
     * registerOutParameterメソッド.
     * 
     * @param parameterIndex
     *            int
     * @param sqlType
     *            sql.Types
     */
    public final void registerOutParameter(
            final int parameterIndex,
            final int sqlType) {
        try {
            this.cstmt.registerOutParameter(parameterIndex, sqlType);
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "registerOutParameter error:",
                    this, e);
        }
    }

    /**
     * registerOutParameterメソッド.
     * 
     * @param parameterIndex
     *            int
     * @param sqlType
     *            sql.Types
     * @param scale
     *            int
     */
    public final void registerOutParameter(
            final int parameterIndex,
            final int sqlType,
            final int scale) {
        try {
            this.cstmt.registerOutParameter(parameterIndex, sqlType, scale);
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "registerOutParameter error:",
                    this, e);
        }
    }

    /**
     * registerOutParameterメソッド.
     * 
     * @param parameterIndex
     *            int
     * @param sqlType
     *            sql.Types
     * @param typeName
     *            int
     */
    public final void registerOutParameter(
            final int parameterIndex,
            final int sqlType,
            final String typeName) {
        try {
            this.cstmt.registerOutParameter(parameterIndex, sqlType, typeName);
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "registerOutParameter error:",
                    this, e);
        }
    }

    /**
     * resultSetメソッド.
     */
    public final void resultSet() {
        try {
            if (this.stmt != null) {
                this.rs = this.stmt.executeQuery(this.sqlMaker.getQuery());
            } else if (this.pstmt != null) {
                this.rs = this.pstmt.executeQuery();
            } else if (this.cstmt != null) {
                this.rs = this.cstmt.executeQuery();
            } else {
                throw new SQLException("Statement is null");
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "resultSet error:",
                    this, e);
        }
    }

    /**
     * rollbackメソッド.
     */
    public final void rollback() {
        try {
            if (!this.isConnectionClose()) {
                if (!this.con.getAutoCommit()) {
                    this.con.rollback();
                }
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "rollback error:",
                    this, e);
        }
    }

    /**
     * @return DbInfo
     */
    private DbInfo select() {
        this.sqlMaker.setQuery(this.sqlMaker.getAutoSql().getSelect());
        return this;
    }

    /**
     * @param aObject
     *            bind data class
     * @return DbInfo
     */
    public DbInfo select(final Object aObject) {
        this.autoSqlClear();
        if (aObject == null) {
            return this.select();
        }
        final Class<?> clazz = aObject.getClass();
        final Table table = Table.class.cast(
                clazz.getAnnotation(Table.class));
        String tableName = "";
        if (table.value().length >= 1) {
            tableName = table.value()[0];
        }
        this.sqlMaker.selectAutoWhere(tableName, aObject);
        return this;
    }

    /**
     * @return DbInfo
     */
    @Override
    public DbInfo selects() {
        this.autoSqlClear();
        // TODO SELECT文生成　sqlMaker.getAutoSql().setSelectへ
        this.sqlMaker.selectJoin();
        return this;
    }

    /**
     * Connectionトランザクションメソッド.<br/>
     * setAutoCommit(false);<--明示的にコミットをしなければ、コミットされない<br/>
     * setAutoCommit(true);<--execute毎に、コミットされる<br/>
     * 
     * @param b
     *            "false"または"true"をセット
     */
    public final void setAutoCommit(final boolean b) {
        try {
            if (this.con != null) {
                this.con.setAutoCommit(b);
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "setAutoCommit error:",
                    this, e);
        }
    }

    /**
     * @param aObject
     *            BindData
     */
    public void setBindData(final Object aObject) {
        this.sqlMaker.setBindData(aObject);

    }

    /**
     * @param aQuery
     *            Sql Query
     * @return DbInfo
     */
    public DbInfo setQuery(final String aQuery) {
        this.sqlMaker.setQuery(aQuery);
        return this;
    }

    /**
     * Connection読み取り専用メソッド.<br/>
     * setReadOnly(false);<--読取専用ではない<br/>
     * setReadOnly(true);<--読取専用<br/>
     * 
     * @param b
     *            "false"または"true"をセット
     */
    public final void setReadOnly(final boolean b) {
        try {
            if (this.con != null) {
                this.con.setReadOnly(b);
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "setReadOnly error:",
                    this, e);
        }
    }

    /**
     * @param aResultDatas the resultDatas to set
     */
    public final void setResultDatas(final List<ResultData> aResultDatas) {
        this.resultDatas = aResultDatas;
    }

    /**
     * データ取得を行うコントロールメソッド.
     * 
     * @see java.sql.Types
     */
    private void srchMapData() {
        this.resultDatas = new ArrayList<ResultData>();
        try {
            this.resultSet();
            int resultCnt = 0;
            while (this.rs.next()) {
                final ResultData resultData = new ResultData();
                final Map<String, Object> data =
                    new WeakHashMap<String, Object>();
                final ResultSetMetaData rsmd =
                    this.rs.getMetaData();
                for (int i = 1; i - 1 < rsmd.getColumnCount(); i++) {
                    this.switchColumnTypeDataPut(data, rsmd, i);
                    resultData.setDatas(data);
                }
                resultCnt++;
                this.resultDatas.add(resultData);
            }
            if (this.cstmt != null) {
                final WeakHashMap<Integer, Object> map =
                    new WeakHashMap<Integer, Object>();
                final Iterator it =
                    this.outData.getMapDatas().entrySet().iterator();
                Map.Entry entry = null;
                while (it.hasNext()) {
                    entry = (Map.Entry) it.next();
                    map.put(Integer.class.cast(entry.getKey()),
                            this.cstmt.getObject(
                                    Integer.class.cast(entry.getKey())
                                            .intValue()));
                }
                this.outData.setDatas(map);
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "srchMapData error:",
                    this, e);
        }
    }

    /**
     * データ取得を行うコントロールメソッド.
     * 
     * @param maxCount
     *            long
     */
    private void srchMapData(final long maxCount) {
        this.resultDatas = new ArrayList<ResultData>();

        try {
            this.resultSet();
            int resultCnt = 0;
            while (this.rs.next()
                && (resultCnt < maxCount)) {
                final ResultData resultData = new ResultData();
                final WeakHashMap<String, Object> data =
                    new WeakHashMap<String, Object>();
                final ResultSetMetaData rsmd =
                    this.rs.getMetaData();
                for (int i = 1; i - 1 < rsmd.getColumnCount(); i++) {
                    this.switchColumnTypeDataPut(data, rsmd, i);
                    resultData.setDatas(data);
                }
                resultCnt++;
                this.resultDatas.add(resultData);
            }
            if (this.cstmt != null) {
                final Map<Integer, Object> map =
                    new WeakHashMap<Integer, Object>();
                final Iterator it =
                    this.outData.getMapDatas().entrySet().iterator();
                Map.Entry entry = null;
                while (it.hasNext()) {
                    entry = (Map.Entry) it.next();
                    map.put(Integer.class.cast(entry.getKey()),
                            this.cstmt.getObject(
                                    Integer.class.cast(entry.getKey())
                                            .intValue()));
                }
                this.outData.setDatas(map);
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "srchMapData error:",
                    this, e);
        }
    }

    /**
     * データ取得を行うコントロールメソッド.
     * 
     * @param bgnRownum
     *            開始行数
     * @param endRownum
     *            終了行数
     */
    private void srchMapData(
            final long bgnRownum,
            final long endRownum) {
        this.resultDatas = new ArrayList<ResultData>();
        try {
            this.resultSet();
            int resultCnt = 0;
            while (this.rs.next()) {
                if ((resultCnt >= bgnRownum - 1)
                    && (resultCnt <= endRownum - 1)) {
                    final ResultData resultData = new ResultData();
                    final Map<String, Object> data =
                        new WeakHashMap<String, Object>();
                    final ResultSetMetaData rsmd =
                        this.rs
                            .getMetaData();
                    for (int i = 1; i - 1 < rsmd.getColumnCount(); i++) {
                        this.switchColumnTypeDataPut(data, rsmd, i);
                        resultData.setDatas(data);
                    }
                    this.resultDatas.add(resultData);
                }
                resultCnt++;
            }
            if (this.cstmt != null) {
                final WeakHashMap<Integer, Object> map =
                    new WeakHashMap<Integer, Object>();
                final Iterator it =
                    this.outData.getMapDatas().entrySet().iterator();
                Map.Entry entry = null;
                while (it.hasNext()) {
                    entry = (Map.Entry) it.next();
                    map.put(Integer.class.cast(entry.getKey()),
                            this.cstmt.getObject(
                            Integer.class.cast(entry.getKey())
                            .intValue()));
                }
                this.outData.setDatas(map);
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "srchMapData error:",
                    this, e);
        }
    }

    /**
     *
     */
    private void statementClose() {
        try {
            if (this.getAutoCommit()) {
                if (this.rs != null) {
                    this.rs.close();
                }
                if (this.stmt != null) {
                    this.stmt.close();
                    this.stmt = null;
                } else if (this.pstmt != null) {
                    this.pstmt.close();
                    this.pstmt = null;
                } else if (this.cstmt != null) {
                    this.cstmt.close();
                    this.cstmt = null;
                }
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "statementClose error:",
                    this, e);
        }
    }

    /**
     * @param data
     *            Map
     * @param rsmd
     *            ResultSetMetaData
     * @param i
     *            int
     */
    private void switchColumnTypeDataPut(
            final Map<String, Object> data,
            final ResultSetMetaData rsmd,
            final int i) {

        try {
            Calendar cal = null;
            switch (rsmd.getColumnType(i)) {
            case Types.NUMERIC:
                data.put(
                        rsmd.getColumnName(i),
                        new Double(this.rs
                        .getDouble(i)));
                break;
            case Types.TIMESTAMP:
                if (this.rs
                        .getTimestamp(i) != null) {
                    cal = Calendar.getInstance();
                    cal.setTime(this.rs
                            .getTimestamp(i));
                }
                data.put(rsmd.getColumnName(i), cal);
                break;
            case Types.DATE:
                if (this.rs
                        .getTimestamp(i) != null) {
                    cal = Calendar.getInstance();
                    cal.setTime(this.rs
                            .getTimestamp(i));
                }
                data.put(rsmd.getColumnName(i), cal);
                break;
            case Types.BOOLEAN:
                data.put(
                        rsmd.getColumnName(i),
                        Boolean
                        .valueOf(this.rs
                        .getBoolean(i)));
                break;
            default:
                data.put(
                        rsmd.getColumnName(i),
                        this.rs.getString(
                        i));
                break;
            }
        } catch (final SQLException e) {
            throw new DbInfoException(
                    "switchColumnTypeDataPut error:",
                    this, e);
        }
    }

    /**
     * @param aClass
     *            table class
     * @return true/false
     */
    public boolean table(final Class<?> aClass) {
        this.sqlMaker.table(aClass);
        final Table table = aClass.getAnnotation(Table.class);
        String create = "";
        if (table.value().length == 2) {
            create = table.value()[1];
        }
        return this.hasTable(create);
    }

    /**
     * @param aClass
     * @return true/false
     */
    @Override
    public boolean tables(final Class<?>... aClass) {
        boolean rtn = false;
        this.sqlMaker.clearTableNames();
        for (final Class<?> class1 : aClass) {
            this.sqlMaker.table(class1);
            rtn = this.hasTable("");
            if (!rtn) {
                break;
            }
        }
        return rtn;
    }

    /**
     * @param aType
     * @return SQL.Types
     */
    private int typeConv(final String aType) {
        if (DbConst.VARCHAR.equals(aType)) {
            return Types.VARCHAR;
        } else if (DbConst.CHAR.equals(aType)) {
            return Types.CHAR;
        } else if (DbConst.INTEGER.equals(aType)) {
            return Types.INTEGER;
        } else if (DbConst.NUMERIC.equals(aType)) {
            return Types.NUMERIC;
        } else if (DbConst.TIMESTAMP.equals(aType)) {
            return Types.TIMESTAMP;
        } else if (DbConst.DATE.equals(aType)) {
            return Types.DATE;
        } else if (DbConst.DOUBLE.equals(aType)) {
            return Types.DOUBLE;
        } else if (DbConst.FLOAT.equals(aType)) {
            return Types.FLOAT;
        } else {
            return Types.OTHER;
        }
    }

    /**
     * @param aSql
     *            sql
     * @param aObjects
     *            data class
     * @return DbInfo
     */
    public DbInfo where(
            final String aSql,
            final Object... aObjects) {
        this.sqlMaker.addWhere(aSql, aObjects);
        return this;
    }

}
